package terra.model;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import terra.Config;
import terra.database.DatabaseFactory;
import terra.definitions.item.ArmorDef;
import terra.definitions.item.ItemDef;
import terra.definitions.item.RecipeDef;
import terra.definitions.item.WeaponDef;
import terra.managers.DefManager;
import terra.managers.ItemManager;
import terra.managers.MapManager;
import terra.managers.ThreadPoolManager;
import terra.model.object.Dynamite;
import terra.model.object.ItemObject;
import terra.model.object.LightObject;
import terra.model.object.Player;
import terra.model.object.item.ArmorItem;
import terra.model.object.item.Item;
import terra.model.object.item.RecipeItem;
import terra.model.object.item.WeaponItem;
import terra.model.object.item.WearItem;
import terra.network.packet.server.InventoryInfo;
import terra.network.packet.server.ObjectDelete;

public class Inventory implements Serializable {
	private static final long serialVersionUID = 8850045366061045380L;
	private WearItem[] slotItems;
	private ArrayList<Item> items;
	public Player owner;
	private int freePlace = 0;
	private int maxCount;
	public int armor;
	public Item constructable = null;
	public boolean changed = false;

	public Inventory(int count, Player player) {
		items = new ArrayList<Item>(count);
		slotItems = new WearItem[Slot.SLOT_COUNT];
		owner = player;
		setMaxCount(count);
	}

	public WeaponItem getWeapon() {
		if (getSlotItems()[Slot.LHAND] != null)
			return (WeaponItem) getSlotItems()[Slot.LHAND];
		return ItemManager.arms;
	}

	public WearItem getWearedItemBySlot(int slot) {
		return getSlotItems()[slot];
	}

	public synchronized boolean removeItem(Item item) {
		boolean ret;
		ret = items.remove(item);
		// inventoryChanged();

		ObjectDelete od = new ObjectDelete();
		od.oId = item.getObjectId();
		//owner.send(od);
		deleteItem(item);
		// owner.send(new ItemInfo(it.getObjectId(),-1, 1, (byte) 2,
		// ItemInfo.INVENTORY));
		changed = true;
		return ret;
	}

	public synchronized boolean addItem(Item item) {
		if (items.size() == getMaxCount()) {
			return false;
		}
		if (items.contains(item)) {
			return false;
		}
		items.add(item);
		changed = true;
		insertItem(item);
		return true;
	}

	public Item getItemByItemId(int itemId) {
		for (Item i : items)
			if (i != null && i.getId() == itemId)
				return i;
		for (int i = 0; i < Slot.SLOT_COUNT; i++)
			if (getSlotItems()[i] != null && getSlotItems()[i].getId() == itemId)
				return getSlotItems()[i];
		return null;
	}

	/** найти предмет по его уникальному номеру. */
	public Item getItemByObjectId(int objectId) {
		if (objectId <= 0)
			return null;
		for (Item i : items)
			if (i != null && i.getObjectId() == objectId)
				return i;
		for (int i = 0; i < Slot.SLOT_COUNT; i++)
			if (getSlotItems()[i] != null
					&& getSlotItems()[i].getObjectId() == objectId)
				return getSlotItems()[i];
		return null;
	}

	public void dropItem(int x2, int y2, int objectId, int count) {
		ItemObject obj = null;
		Item it = getItemByObjectId(objectId);
		if (it == null) {
			System.out.println("player try drop item, that he have not!");
			return;
		}
		if (it.getDef().isStackable && it.getCount() - count < 0) {
			System.out.println("player try drop more items, then he have!");
			return;
		}
		if (owner.distanceTo(x2, y2) > Config.tileSize * 7) {
			System.out.println("player try drop too far! distance: "
					+ owner.distanceTo(x2, y2));
			return;
		}
		boolean remove = true;
		if (it.getDef().isStackable && it.getCount() > count)
			remove = false;
		if (DefManager.allItems.get(it.getId()).instance != null)
			try {
				obj = (ItemObject) Class.forName(
						"terra.model.object."
								+ DefManager.allItems.get(it.getId()).instance)
						.newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
		else
			obj = new ItemObject();
		System.out.println("is " + obj.getClass());
		obj.setWidth(Config.tileSize);
		obj.setHeight(Config.tileSize);
		obj.setName(it.getName());
		int mapIndex = MapManager.getInstance().getMapIndex(
				(int) (owner.getX()) / Config.tileSize);
		int mapX = (int) (owner.getX()) / Config.tileSize - mapIndex
				* Config.mapWidth;
		int mapY = (int) (owner.getY() / Config.tileSize);

		obj.setY(owner.getY());
		obj.setX(mapX * Config.tileSize + mapIndex * Config.mapWidth
				* Config.tileSize);
		while (MapManager.instance.getMaps().get(mapIndex).getMap()[mapX][mapY] == null
				|| MapManager.instance.getMaps().get(mapIndex).getMap()[mapX][mapY]
						.canMove()) {
			mapY++;
			// System.out.println("drop mapObject "+
			// MapManager.instance.getMaps().get(mapIndex).map[mapX][mapY]);
		}
		obj.setGroundTileX(mapX);
		obj.setGroundTileY(mapY);
		// obj.setY(obj.getTile().getY()-Config.tileSize);

		obj.isDropped = true;
		// obj.droppedBy = this.owner;
		// System.out.println("drop coords " +mapIndex +"_"+ mapX +"_"+ mapY
		// +"_"+ owner.getX()+"_"+ owner.getY() +"_"+ obj.getX()+"_"+
		// obj.getY());
		if (!remove) {
			Item i = new Item(World.getNextId(), it.getDef(), null);
			i.setCount(count);
			//(obj).item = i;
			obj.setDefId(it.getDef().id);
			((ItemObject) obj).setItemCount(count);
		} else{
			obj.setDefId(it.getDef().id);
			((ItemObject) obj).setItemCount(it.getCount());
		}
		if (it.getDef().getBoolProperty("workplace"))
			obj.setWorkplace(true);
		if (obj instanceof LightObject)
			((LightObject) obj).init(it.getDef());
		if (obj instanceof Dynamite)
			((Dynamite) obj).owner = owner;
		obj.setPickupable(it.getDef().isPickupable);
		World.getInstance().addObject(obj.getObjectId(), obj);
		if (!(obj instanceof Dynamite))
			obj.insertStaticObject();
		if (remove)
			removeItem(it);
		else
			changeCount(it.getId(), -count);
	}

	public void inventoryChanged() {
		// updateStackable();
		if (changed) {
			try {
				owner.checkShortCuts();
				// updateWeared();
				// System.out.println("inventoryChanged");
				InventoryInfo info = new InventoryInfo();
				info.oId = owner.getObjectId();
				info.maxCount = getMaxCount();
				if (constructable != null)
					info.constructable = constructable.getId();

				for (int i = 0; i < Slot.SLOT_COUNT; i++) {
					WearItem wear = getSlotItems()[i];
					info.slotItemIds[i] = (wear == null ? -1 : wear.getId());
					info.slotOIds[i] = (wear == null ? -1 : wear.getObjectId());
				}
				info.invCounts = new int[getMaxCount()];
				info.invItemIds = new int[getMaxCount()];
				info.invOIds = new int[getMaxCount()];

				for (int i = 0; i < getMaxCount(); i++) {
					Item item = null;
					if (i < getItems().size())
						item = getItems().get(i);
					info.invItemIds[i] = (item == null ? -1 : item.getId());
					info.invOIds[i] = (item == null ? -1 : item.getObjectId());
					info.invCounts[i] = (item == null ? -1 : item.getCount());
				}
				owner.send(info);
			} catch (Exception e) {
				e.printStackTrace();
			}
			changed = false;
		}
	}

	public synchronized void wearItem(WearItem item, boolean send) {
		if (item == null)
			return;
		int slot = 0;
		slot = item.slot;
		if (slot == -1)
			return;
		if (getWearedItemBySlot(slot) == null) {
			getSlotItems()[slot] = item;
		} else {
			if (getWearedItemBySlot(slot) == item)
				return;
			unWearItem(getWearedItemBySlot(slot), false);
			// addItem(getWearedItemBySlot(slot));
			// getWearedItemBySlot(slot).isWeared = false;
			getSlotItems()[slot] = item;
		}
		items.remove(item);
		item.isWeared = true;
		updateWeared();
		updateItem(item,slot);
		changed = true;
	}

	public synchronized void unWearItem(WearItem item, boolean send) {
		if (item == null)
			return;
		if (item.getId() == -1)
			return;
		if (items.size() == getMaxCount())
			return;
//		if (addItem(it)) {
		items.add(item);
		item.isWeared = false;
		getSlotItems()[item.slot] = null;
//		}
		updateWeared();
		updateItem(item,-1);
		// if (send)
		// owner.send(new ItemInfo(-1,-1, 1, (byte) it.slot, ItemInfo.WEARED));
		changed = true;
	}

	/** Обновляем параметры одетых вещей. */
	private void updateWeared() {
		armor = 0;
		for (int i = 0; i < Slot.SLOT_COUNT; i++) {
			if (i == Slot.LHAND || getSlotItems()[i] == null)
				continue;
			armor += ((ArmorItem) getSlotItems()[i]).armor;
		}
		owner.getStat().calcStats();
		// owner.sendPlayerInfo = true;
	}

	public Item createItem(ItemDef def, int objectId) {
		Item item = null;
		if (def instanceof WeaponDef)
			item = new WeaponItem(objectId, (WeaponDef) def, this);
		else if (def instanceof ArmorDef)
			item = new ArmorItem(objectId, (ArmorDef) def, this);
		else if (def instanceof RecipeDef)
			item = new RecipeItem(objectId, (RecipeDef) def, this);
		else
			item = new Item(objectId, def, this);
		return item;
	}

	public boolean pickupItem(int itemId, int count) {
		ItemDef def = DefManager.allItems.get(itemId);
		System.out.println(def);
		if (def.isStackable) {
			if (getItemByItemId(itemId) != null) {
				changeCount(itemId, count);
				return true;
				// getItemByItemId(itemId).addCount(count);
			} else {
				Item it = createItem(def, World.getNextId());
				it.setCount(count);
				return pickupItem(it);
			}
		} else {
			for (int i = 0; i < count; i++)
				pickupItem(createItem(def, World.getNextId()));
			return true;
		}
	}

	public boolean pickupItem(Item item) {
		boolean result = false;
		if (item == null)
			return result;
		if (item.getDef() == null)
			return result;
		if (item.getDef().isStackable && getItemByItemId(item.getId()) != null) {
			changeCount(item.getId(), item.getCount());
			// getItemByItemId(item.getId()).addCount(item.getCount());
			result = true;
		} else {
			if (addItem(item))
				result = true;
		}
		// inventoryChanged();
		return result;
	}

	public int getMaxCount() {
		return maxCount;
	}

	public void setMaxCount(int maxCount) {
		this.maxCount = maxCount;
	}

	public int getFreePlace() {
		return freePlace;
	}

	public void setFreePlace(int freePlace) {
		this.freePlace = freePlace;
	}

	public synchronized ArrayList<Item> getItems() {
		return items;
	}

	public WearItem[] getSlotItems() {
		return slotItems;
	}

	public void changeCount(int id, int i) {
		final Item item = getItemByItemId(id);
		if (item != null && item.getDef().isStackable) {
			item.addCount(i);
			changed = true;
			if (item.getCount() > 0) {
				ThreadPoolManager.getInstance().executeTask(new Runnable(){

					@Override
					public void run() {
						try (Connection con = DatabaseFactory.getInstance()
								.getConnection();
								PreparedStatement statement = con
										.prepareStatement("UPDATE items SET count=? WHERE objectId=?")) {
							statement.setInt(1, item.getCount());
							statement.setInt(2, item.getObjectId());
							statement.execute();
							statement.close();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}});

			} else {
				removeItem(item);
			}
		}
	}

	public void setConstructable(Item constructable) {
		this.constructable = constructable;
		changed = true;
	}

	public void addItem(ResultSet rs) throws SQLException {
		ItemDef def = DefManager.allItems.get(rs.getInt("itemId"));
		Item it = createItem(def, rs.getInt("objectId"));
		it.setCount(rs.getInt("count"));
		pickupItem(it);
		if (rs.getInt("slot") != -1) {
			if (it instanceof WearItem)
				wearItem((WearItem) it, false);
		}
	}

	public void loadInventory() {
		try (Connection con = DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con
						.prepareStatement("SELECT * FROM items WHERE ownerId=?")) {
			statement.setInt(1, owner.getObjectId());
			ResultSet rs = statement.executeQuery();
			while (rs.next()) {
				addItem(rs);
			}
			rs.close();
			statement.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void saveInventory() {
		try (Connection con = DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con
						.prepareStatement("REPLACE INTO items (objectId,ownerId,itemId,count,slot) VALUES (?,?,?,?,?)");) {
			for (Item item : getItems()) {
				statement.clearParameters();
				statement.setInt(1, item.getObjectId());
				statement.setInt(2, owner.getObjectId());
				statement.setInt(3, item.getId());
				statement.setInt(4, item.getCount());
				statement.setInt(5, -1);
				statement.execute();
			}
			for (int i = 0; i < getSlotItems().length; i++) {
				Item item = getSlotItems()[i];
				if (item == null)
					continue;
				statement.clearParameters();
				statement.setInt(1, item.getObjectId());
				statement.setInt(2, owner.getObjectId());
				statement.setInt(3, item.getId());
				statement.setInt(4, item.getCount());
				statement.setInt(5, i);
				statement.execute();
			}

			statement.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	void insertItem(final Item item){
		ThreadPoolManager.getInstance().executeTask(new Runnable(){

			@Override
			public void run() {
				try (Connection con = DatabaseFactory.getInstance().getConnection();
						PreparedStatement statement = con
								.prepareStatement("REPLACE INTO items (objectId,ownerId,itemId,count,slot) VALUES (?,?,?,?,?)");) {
						statement.clearParameters();
						statement.setInt(1, item.getObjectId());
						statement.setInt(2, owner.getObjectId());
						statement.setInt(3, item.getId());
						statement.setInt(4, item.getCount());
						statement.setInt(5, -1);
						statement.execute();
					statement.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}});
	}
	
	void updateItem(final Item item,final int slot){
		ThreadPoolManager.getInstance().executeTask(new Runnable(){

			@Override
			public void run() {
				try (Connection con = DatabaseFactory.getInstance().getConnection();
						PreparedStatement statement = con
								.prepareStatement("REPLACE INTO items (objectId,ownerId,itemId,count,slot) VALUES (?,?,?,?,?)");) {
						statement.clearParameters();
						statement.setInt(1, item.getObjectId());
						statement.setInt(2, owner.getObjectId());
						statement.setInt(3, item.getId());
						statement.setInt(4, item.getCount());
						statement.setInt(5, slot);
						statement.execute();
					statement.close();
				} catch (Exception e) {
					e.printStackTrace();
				}	
			}});
	}
	
	void deleteItem(final Item item){
		ThreadPoolManager.getInstance().executeTask(new Runnable(){

			@Override
			public void run() {
				try (Connection con = DatabaseFactory.getInstance().getConnection();
						PreparedStatement statement = con
								.prepareStatement("DELETE FROM items WHERE objectId=?");) {
						statement.clearParameters();
						statement.setInt(1, item.getObjectId());
						statement.execute();
					statement.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}});
	}

}
